import asyncio
import json
import logging
import os
import platform
import re

from aiohttp import web
from aiortc import RTCPeerConnection, RTCSessionDescription
from aiortc.contrib.media import MediaPlayer, MediaRelay
from aiortc.rtcrtpsender import RTCRtpSender

ROOT = os.path.dirname(__file__)

relay = None
webcam = None


def create_local_tracks(play_from, decode):
    global relay, webcam

    if play_from:
        player = MediaPlayer(play_from, decode=decode)
        return player.audio, player.video
    else:
        options = {"framerate": "30", "video_size": "640x480"}
        if relay is None:
            if platform.system() == "Windows":
                # 低延迟优化
                options["audio_buffer_size"] = "100"
                options["r"] = "60"
                file = "video=" + args.video_device_name
                if args.audio_device_name:
                    file = file + ":audio=" + args.audio_device_name
                webcam = MediaPlayer(
                   file, format="dshow", options=options
                )
            relay = MediaRelay()
        return relay.subscribe(webcam.audio), relay.subscribe(webcam.video)


def force_codec(pc, sender, forced_codec):
    kind = forced_codec.split("/")[0]
    codecs = RTCRtpSender.getCapabilities(kind).codecs
    transceiver = next(t for t in pc.getTransceivers() if t.sender == sender)
    transceiver.setCodecPreferences(
        [codec for codec in codecs if codec.mimeType == forced_codec]
    )


async def index(request):
    content = open(os.path.join(ROOT, "web/index.html"), "r", encoding="utf-8").read()
    return web.Response(content_type="text/html", text=content)


async def offer(request):
    user_agent = request.headers.getone("User-Agent", '')
    is_mobile = len(re.findall("iPhone|Android", user_agent)) > 0
    params = await request.json()
    offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])

    pc = RTCPeerConnection()
    pcs.add(pc)

    @pc.on("connectionstatechange")
    async def on_connectionstatechange():
        print("Connection state is %s" % pc.connectionState)
        if pc.connectionState == "failed":
            await pc.close()
            pcs.discard(pc)

    # open media source
    audio, video = create_local_tracks(
        args.play_from, decode=not args.play_without_decoding
    )

    if audio and not is_mobile:
        audio_sender = pc.addTrack(audio)
        if args.audio_codec:
            force_codec(pc, audio_sender, args.audio_codec)
        elif args.play_without_decoding:
            raise Exception("You must specify the audio codec using --audio-codec")

    if video:
        video_sender = pc.addTrack(video)
        if args.video_codec:
            force_codec(pc, video_sender, args.video_codec)
        elif args.play_without_decoding:
            raise Exception("You must specify the video codec using --video-codec")

    await pc.setRemoteDescription(offer)

    answer = await pc.createAnswer()
    await pc.setLocalDescription(answer)

    return web.Response(
        content_type="application/json",
        text=json.dumps(
            {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
        ),
    )


pcs = set()


async def on_shutdown(app):
    # close peer connections
    coros = [pc.close() for pc in pcs]
    await asyncio.gather(*coros)
    pcs.clear()

class Config:
    audio_codec = "audio/opus"
    video_codec = "video/h264"
    video_device_name = "Integrated Camera"
    # 每个人的麦克风的名字不一样
    audio_device_name = "麦克风阵列 (Realtek(R) Audio)"
    play_from = None
    play_without_decoding = None

if __name__ == "__main__":
    args = Config()
    logging.basicConfig(level=logging.INFO)

    # 使用aiohttp构建一个web应用
    app = web.Application()
    app.on_shutdown.append(on_shutdown)
    app.router.add_get("/", index)
    # 路由静态文件
    app.router.add_static("/", path="web",  name="web")
    app.router.add_post("/offer", offer)
    web.run_app(app, host='localhost', port='8080', ssl_context=None)
